package com.axonactive.yolo.monitor;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.axonactive.yolo.domain.SubscribedFlight;
import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.Parameters;
import com.axonactive.yolo.exception.SubcribedFlightNotFoundException;
import com.axonactive.yolo.service.internal.FlightRequesterService;
import com.axonactive.yolo.util.CONSTANTS;

/**
 * The flight subject is responsible for detecting the change of price and
 * schedule
 * 
 * @author pqtrung
 * 
 */
public class FlightObserverable implements Observerable {

	private Observer observer;
	private List<Flight> flights;
	private List<SubscribedFlight> cancelledFlights;
	private List<SubscribedFlight> changePriceFlights;
	private List<SubscribedFlight> changeScheduleFlights;
	private final static Logger LOGGER = Logger
			.getLogger(FlightObserverable.class);
	private FlightRequesterService flightRequesterService = new FlightRequesterService();

	@Override
	public boolean detect() throws SubcribedFlightNotFoundException {
		if (observer == null) {
			return false;
		}
		if (observer instanceof FlightSubscriber) {
			
			cancelledFlights=new ArrayList<SubscribedFlight>();
			changePriceFlights=new ArrayList<SubscribedFlight>();
			changeScheduleFlights=new ArrayList<SubscribedFlight>();
			FlightSubscriber flightSubscriber = (FlightSubscriber) observer;
			//check the flag to access the table
//			boolean lock=flightSubscriber.getLock();
//			if (lock==true){
//				return true;
//			}
//			//set table is locked
//			flightSubscriber.setLock(true);
			List<Parameters> pars = flightSubscriber.getListOfParameters();
			LOGGER.info("size of list parameters: " + pars.size());
			try{
				for (Parameters par : pars) {
					try {
						LOGGER.info("schedule: " + par.getOutBound() + "/"
								+ par.getInBound());
						Thread.sleep(30000);
						flights = flightRequesterService.sendRequest(par);
					} catch (ParseException ex) {
						LOGGER.error("parse response problem: ", ex);
						continue;
					} catch (Exception ex) {
						LOGGER.error("send request problem: ", ex);
						continue;
					}
					if (flights.size() == 0) {
						continue;
					}
					// ask flight subscriber what flights need to be compared
					List<SubscribedFlight> subs = flightSubscriber
							.getListOfSubcribedFlight(par);
					LOGGER.info("Parameter with schedule: " + par.getOutBound()
							+ "-" + par.getInBound() + " has size: "
							+ subs.size());
					// compare
					compareFlights(subs, flights);
					// notify to subscriber
					flightSubscriber.update(changePriceFlights,changeScheduleFlights, cancelledFlights);
				}
			}
			catch(Exception e){
				LOGGER.info("detect problem",e);
			}
			finally{
				//set table is unlocked
//				flightSubscriber.setLock(false);
			}

		}
		return true;
	}

	@Override
	public void register(Observer observer) {
		this.observer = observer;
	}

	@Override
	public void unRegister(Observer observer) {
		this.observer = null;
	}

	private void compareFlights(List<SubscribedFlight> subs,
			List<Flight> flights2) {
		int size = subs.size();
		cancelledFlights.clear();
		changePriceFlights.clear();
		changeScheduleFlights.clear();
		LOGGER.info("compare Flights - size subscribed flights:" + size);
		for (int i = 0; i < size; i++) {
			SubscribedFlight sub = subs.get(i);
			int detector = hasChange(sub, flights2);
			if ((detector & CONSTANTS.DETECT_CHANGE_PRICE) == CONSTANTS.DETECT_CHANGE_PRICE)
			{
				changePriceFlights.add(sub);
			}
			if ((detector & CONSTANTS.DETECT_CHANGE_SCHEDULE) == CONSTANTS.DETECT_CHANGE_SCHEDULE) {
				changeScheduleFlights.add(sub);
			}
			if ((detector & CONSTANTS.DETECT_CHANGE_CANCELLED) == CONSTANTS.DETECT_CHANGE_CANCELLED) {
				cancelledFlights.add(sub);
			}
			subs.remove(i);
			size--;
			i--;
		}
		LOGGER.info("compare flights - cancelledFlights:  "+ cancelledFlights.size());
		LOGGER.info("compare flights - changeScheduleFlights:  "+ changeScheduleFlights.size());
		LOGGER.info("compare flights - changePriceFlights:  "+ changePriceFlights.size());
	}

	private int hasChange(SubscribedFlight sub, List<Flight> flights2) {
		LOGGER.info("flight id of sub:" + sub.getFlightId());
		boolean isCanceled = true;
		int flag = CONSTANTS.DETECT_NON_CHANGE;
		for (Flight flight : flights2) {
			if (sub.getFlightId().equals(flight.getFlightID())) {
				isCanceled = false;
				LOGGER.info("compare two flighs have same flight id= "
						+ sub.getFlightId());
				LOGGER.info("subscribed flight infor:");
				LOGGER.info("getOutBoundDepartureTime"
						+ sub.getOutBoundDepartureTime());
				LOGGER.info("getOutBoundArrivalTime"
						+ sub.getOutBoundArrivalTime());
				LOGGER.info("getInBoundDepartureTime"
						+ sub.getInBoundDepartureTime());
				LOGGER.info("getInBoundArrivalTime"
						+ sub.getInBoundArrivalTime());
				LOGGER.info("price: " + sub.getPrice());
				// compare price
				if (sub.getPrice() != Double.parseDouble(flight.getPrice()
						.getAmount())) {
					sub.setPrice(Double.parseDouble(flight.getPrice()
							.getAmount()));
					flag =flag|CONSTANTS.DETECT_CHANGE_PRICE;
				}
				// compare outbound departure time
				if (!sub.getOutBoundDepartureTime().equals(
						flight.getOutBound().getDepartAirports().get(0)
								.getDepartureTimeByHHmm())) {
					sub.setOutBoundDepartureTime(flight.getOutBound()
							.getDepartAirports().get(0)
							.getDepartureTimeByHHmm());
					flag =flag|CONSTANTS.DETECT_CHANGE_SCHEDULE;
				}
				// compare outbound arrival time
				String outBoundArrivalTime = flight
						.getOutBound()
						.getArriveAirports()
						.get(flight.getOutBound().getArriveAirports().size() - 1)
						.getArrivalTimeByHHmm();

				LOGGER.info("flight - outbound departure time: "
						+ flight.getOutBound().getDepartAirports().get(0)
								.getDepartureTimeByHHmm());
				LOGGER.info("flight - outbound arrival time: "
						+ outBoundArrivalTime);
				if (!sub.getOutBoundArrivalTime().equals(outBoundArrivalTime)) {
					sub.setOutBoundArrivalTime(outBoundArrivalTime);
					flag =flag|CONSTANTS.DETECT_CHANGE_SCHEDULE;
				}
				if (sub.getTypeOfTrip().equals(CONSTANTS.ROUND_TRIP)) {
					// compare inbound departure time
					LOGGER.info("flight - inbound departure time: "
							+ flight.getInBound().getDepartAirports().get(0)
									.getDepartureTimeByHHmm());
					if (!sub.getInBoundDepartureTime().equals(
							flight.getInBound().getDepartAirports().get(0)
									.getDepartureTimeByHHmm())) {
						sub.setInBoundDepartureTime(flight.getInBound()
								.getDepartAirports().get(0)
								.getDepartureTimeByHHmm());
						flag =flag|CONSTANTS.DETECT_CHANGE_SCHEDULE;
					}
					// compare inbound arrival time
					String inBoundArrivalTime = flight
							.getInBound()
							.getArriveAirports()
							.get(flight.getInBound().getArriveAirports().size() - 1)
							.getArrivalTimeByHHmm();

					LOGGER.info("flight - inbound arrival time: "
							+ inBoundArrivalTime);
					if (!sub.getInBoundArrivalTime().equals(inBoundArrivalTime)) {
						sub.setInBoundArrivalTime(inBoundArrivalTime);
						flag = flag|CONSTANTS.DETECT_CHANGE_SCHEDULE;
					}
				}
				// end if statement
				// if you go to here, it means that we don't dectect any change,
				// so break loop for
				break;
			}
			// end loop for
		}
		return isCanceled ? CONSTANTS.DETECT_CHANGE_CANCELLED : flag;
	}
}
